/*
 * Copyright (c) 2005, 2014 vacoor
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 */
package org.vacoor.nothing.common.util;

import com.google.common.collect.Lists;

import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 对 JDK 正则表达式的简单封装, 提供正则的匹配, 替换, 提取操作
 * <p/>
 *
 * @author vacoor
 * @see java.util.regex
 */
public abstract class Regexs {
    // 非 ASCII 数字, eg: 中文全角数字
    // public static final String NON_ASCII_DIGIT = "[\\p{javaDigit}&&[^0-9]]";

    public static final int DEFAULT_PATTERN_FLAG = 0;

    private static final Object monitor = new Object();
    private static Map<String, Pattern> cache = new WeakHashMap<String, Pattern>();

    /**
     * 测试给定字符序列能否匹配给定正则
     * 使用默认匹配选项({@link #DEFAULT_PATTERN_FLAG}, {@link #matches(CharSequence, String, int)})
     *
     * @param charseq
     * @param regex
     */
    public static boolean matches(CharSequence charseq, String regex) {
        return matches(charseq, regex, DEFAULT_PATTERN_FLAG);
    }

    /**
     * 测试给定字符序列能否匹配给定正则, 允许使用匹配标识
     * eg: matches("test", "TEST", Pattern.CASE_INSENSITIVE)
     * <p/>
     * 备注一下, 经常测试不包含某个字符串正则如下:
     * ^(?:(?!test).)*$
     * 当然也可以使用顺序环视
     * 这里没有使用逆向环视是因为逆向环视很多语言支持不是很好所以习惯了
     *
     * @param charseq 要测试的字符序列
     * @param regex   用于匹配正则表达式
     */
    public static boolean matches(CharSequence charseq, String regex, int flag) {
        return cachedPattern(regex, flag).matcher(charseq).matches();
    }

    /**
     * 使用默认匹配选项({@link #DEFAULT_PATTERN_FLAG}, {@link #grep(String, String, int)})
     *
     * @param charseq
     * @param regex
     * @param replacement
     */
    public static String replaceFirst(CharSequence charseq, String regex, String replacement) {
        return replaceFirst(charseq, regex, replacement, DEFAULT_PATTERN_FLAG);
    }

    /**
     * 替换
     * <pre>
     * String charseq =
     *      "SELECT * \r\n" +
     *      "FROM \r\n" +
     *      "PS_DOMAIN";
     * charseq = replaceFirst(charseq, "^(.*\\s*from\\s+)(.*)(\\s*.*)", "$1 -- $2 -- $3", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE);
     * System.out.println(charseq);
     *      SELECT *
     *      FROM
     *       -- PS_DOMAIN --
     * </pre>
     *
     * @param charseq
     * @param regex
     * @param replacement
     * @param flag
     * @return
     */
    public static String replaceFirst(CharSequence charseq, String regex, String replacement, int flag) {
        return cachedPattern(regex, flag).matcher(charseq).replaceFirst(replacement);
    }

    /**
     * 使用默认匹配选项({@link #DEFAULT_PATTERN_FLAG}, {@link #grep(String, String, int)})
     *
     * @param charseq
     * @param regex
     * @param replacement
     * @return
     */
    public static String replaceAll(CharSequence charseq, String regex, String replacement) {
        return replaceAll(charseq, regex, replacement, DEFAULT_PATTERN_FLAG);
    }

    public static String replaceAll(CharSequence charseq, String regex, String replacement, int flag) {
        return cachedPattern(regex, flag).matcher(charseq).replaceAll(replacement);
    }

    /**
     * 获取字符序列中匹配到正则中第 group 个捕获组的内容
     * 使用默认匹配选项({@link #DEFAULT_PATTERN_FLAG}, {@link #group(CharSequence, String, int, int)})
     *
     * @param charseq
     * @param regex
     * @param group
     */
    public static String[] group(CharSequence charseq, String regex, int group) {
        return group(charseq, regex, group, DEFAULT_PATTERN_FLAG);
    }

    /**
     * 获取字符序列中匹配到正则中第 group 个捕获组的内容
     *
     * @param charseq
     * @param regex
     * @param group
     */
    public static String[] group(CharSequence charseq, String regex, int group, int flag) {
        Matcher matcher = cachedPattern(regex, flag).matcher(charseq);
        List<String> result = Lists.newLinkedList();

        while (matcher.find()) {
            result.add(matcher.group(group));
        }

        return result.toArray(new String[result.size()]);
    }

    /**
     * 获取给定正则模式对字符序列的匹配结果
     * 使用默认匹配选项({@link #DEFAULT_PATTERN_FLAG}, {@link #grep(String, String, int)})
     *
     * @param charseq
     * @param regex
     * @return
     */
    public static String[][] grep(String charseq, String regex) {
        return grep(charseq, regex, DEFAULT_PATTERN_FLAG);
    }

    /**
     * 获取给定正则模式对字符序列的匹配结果
     * <p/>
     * 返回一个二维数组
     * 第一维为模式第N次获取结果
     * 第二维第0个元素为整个模式匹配结果, 之后第I个元素为第I个捕获组捕获到的结果
     * eg:
     * grep("abc123def456", "[a-z]+([0-9]+)")
     * 得到如下二维数组
     * abc123, 123
     * def456, 456
     *
     * @param charseq 要捕获的字符序列
     * @param regex   使用的正则表达式
     * @return
     */
    public static String[][] grep(String charseq, String regex, int flag) {
        List<String[]> result = Lists.newLinkedList();

        Matcher matcher = cachedPattern(regex, flag).matcher(charseq);
        while (matcher.find()) {
            int gc = matcher.groupCount();

            String[] m = new String[gc + 1];
            for (int i = 0; i <= gc; i++) {
                m[i] = matcher.group(i);
            }
            result.add(m);
        }
        return result.toArray(new String[result.size()][]);
    }

    /**
     * 获取缓存给定正则的 Pattern 对象
     */
    public static Pattern cachedPattern(String regex, int flag) {
        final String key = regex + flag;

        Pattern pattern = cache.get(key);
        if (pattern != null) {
            return pattern;
        }

        synchronized (monitor) {
            if (pattern == null) {
                cache.put(key, pattern = Pattern.compile(regex, flag));
            }
        }
        return pattern;
    }

    private Regexs() {
    }
}
